package me.guyongquan.cal10npropwriter;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.Writer;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Map.Entry;

public class PropertiesUpdater {
	private Map<Locale, String> current = new HashMap<Locale, String>();
	private Map<Locale, Properties> propMap = new HashMap<Locale, Properties>();
	private String currentKey;
	private Properties enProp;
	private Properties defaultProp;
	private Properties zhProp;
	private File dir;
	private String path;
	private static final Locale DEFAULT = new Locale("default");

	public PropertiesUpdater(File mainResourcesDir, String baseName) {
		path = baseName.replace('.', '/');
		this.dir = mainResourcesDir;
		enProp = load(mainResourcesDir, path, "_en_US");
		defaultProp = load(mainResourcesDir, path, "");
		zhProp = load(mainResourcesDir, path, "_zh_CN");
		propMap.put(Locale.SIMPLIFIED_CHINESE, zhProp);
		propMap.put(Locale.US, enProp);
		propMap.put(DEFAULT, defaultProp);
	}

	private Properties load(File mainResourcesDir, String baseName,
			String lang) {
		
		File file = new File(mainResourcesDir, baseName + lang + ".properties");
		Properties properties = new Properties();
		properties.load(file);
		return properties;
	}

	public void saveElement() {
		doSaveElement();
		current.clear();
		currentKey = null;
	}

	private void doSaveElement() {
		if(current.isEmpty() || currentKey == null) {
			return;
		}
		if(current.size() == 1) {
			Entry<Locale, String> entry = current.entrySet().iterator().next();
			updateByOne(entry.getKey(), entry.getValue());
			return;
		}
		Iterator<Entry<Locale, String>> it = current.entrySet().iterator();
		String defaultValue = null;
		String enUSValue = null;
		while(it.hasNext()) {
			Entry<Locale, String> entry = it.next();
			Properties prop = getProp(entry.getKey());
			prop.put(currentKey, entry.getValue());
			if(Locale.US.equals(entry.getKey())) {
				enUSValue = entry.getValue();
			} else if(DEFAULT.equals(entry.getKey())) {
				defaultValue = entry.getValue();
			}
		}
		if(defaultValue == null && enUSValue != null) {
			defaultProp.put(currentKey, enUSValue);
		} else if(enUSValue == null && defaultValue != null) {
			enProp.put(currentKey, defaultValue);
		}
	}

	private Properties getProp(Locale key) {
		Properties result = propMap.get(key);
		if(result == null) {
			File file = new File(dir, path + key.toString() + ".properties");
			result = new Properties();
			result.load(file);
			propMap.put(key, result);
		}
		return result;
	}

	private void updateByOne(Locale locale, String value) {
		Properties prop = propMap.get(locale);
		if(prop == null) {
			return;
		}
		Object existValue = prop.put(currentKey, value);
		Iterator<Entry<Locale, Properties>> it = propMap.entrySet().iterator();
		while(it.hasNext()) {
			Entry<Locale, Properties> entry = it.next();
			if(entry.getKey().equals(locale)) {
				continue;
			}
			prop = entry.getValue();
			if(!prop.contains(currentKey) || (existValue != null && existValue.equals(prop.get(currentKey)))) {
				prop.put(currentKey, value);
			}
		}
	}
	
	public void setCurrentKey(String value) {
		if(currentKey != null) {
			saveElement();
		}
		currentKey = value;
	}

	public void update(Locale locale, String message) {
		Locale key = locale == null ? DEFAULT : locale;
		if(!current.containsKey(key)) {
			current.put(key, message);
		}
	}

	public void save() {
		saveElement();
		for(Properties prop : propMap.values()) {
			prop.save();
		}
	}
	
	@SuppressWarnings("serial")
	private static class Properties extends java.util.Properties {
		private File file;

		void load(File file) {
			this.file = file;
			if (file.exists()) {
				try {
					BufferedReader reader = new BufferedReader(
							new InputStreamReader(new FileInputStream(file),
									"utf-8"));
					super.load(reader);
				} catch (IOException e) {

				}
			}
		}
		
		void save() {
			try {
				doSave();
			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
		@SuppressWarnings({ "unchecked", "serial" })
		private void doSave() throws IOException {
			final List<Object> sortedKeys = new LinkedList<Object>();
			if (file.exists()) {
				final List<Object> storedKeys = new LinkedList<Object>();
				BufferedReader reader = new BufferedReader(
						new InputStreamReader(new FileInputStream(file),
								"utf-8"));
				try {
					String data;
					while ((data = reader.readLine()) != null) {
						if (data.startsWith("#")) {
							continue;
						}
						String key = data.substring(0, data.indexOf('='));
						storedKeys.add(key);
					}
				} finally {
					reader.close();
				}
				if (!storedKeys.isEmpty()) {
					sortedKeys.addAll(merge((List<String>) (Object)storedKeys, (List<String>) (Object)new LinkedList<Object>(keySet())));
				}
			}
			if (sortedKeys.isEmpty()) {
				sortedKeys.addAll(keySet());
				Collections.sort((List<String>) (Object) sortedKeys);
			}
			java.util.Properties toStore = new java.util.Properties() {
				public synchronized java.util.Enumeration<Object> keys() {
					return Collections.enumeration(sortedKeys);
				};
			};
			toStore.putAll(this);
			Writer writer = new BufferedWriter(new OutputStreamWriter(
					new FileOutputStream(file), "utf-8")) {
				private boolean skipping;
				private boolean newLine = false;

				public void write(String str) throws IOException {
					if (str.startsWith("#")) {
						skipping = true;
						return;
					}
					if(newLine) {
						super.write("\r\n");
					} else {
						newLine = true;
					}
					
					StringBuilder builder = new StringBuilder();
					char[] chs = str.toCharArray();
					for (char c : chs) {
						if (c > 128) {
							builder.append("\\u")
									.append(Integer.toHexString(c));
						} else {
							builder.append(c);
						}
					}
					super.write(builder.toString());
				};

				@Override
				public void newLine() throws IOException {
					return;
				}
			};
			try {
				toStore.store(writer, null);
			} finally {
				writer.close();
			}
		}

		private Collection<Object> merge(final List<String> storedKeys, List<String> curKeys) {
			List<String> result = new LinkedList<String>(curKeys);
			Collections.sort(result, new Comparator<String>(){
				@Override
				public int compare(String o1, String o2) {
					int index1 = storedKeys.indexOf(o1);
					int index2 = storedKeys.indexOf(o2);
					if(index1 != -1 && index2 != -1) {
						return index1 - index2;
					}
					if(index1 == -1 && index2 == -1) {
						return o2.compareTo(o2);
					}
					if(index1 == -1) {
						return 1;
					}
					return -1;
				}
			});
			return new LinkedList<Object>(result);
		}
	}

}
